using System;
using System.Collections.Generic;
using System.ServiceModel;
using RPGOnline.Remoting;
using RPGOnline.Remoting.Entities;

namespace RPGOnline.Server
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single, IncludeExceptionDetailInFaults = true)]
    public class ChatServer : IChat
    {
        private SessionContext m_sessionContext;
        private ConnectionsManager m_connectionsManager;

        public ChatServer()
        {
            m_sessionContext = new SessionContext();
            m_connectionsManager = new ConnectionsManager(m_sessionContext);
        }

	    public void KeepAlive()
	    {
	    }

	    public void RegisterUser(User user, bool asMaster)
        {
            IChatCallback callbackChannel = OperationContext.Current.GetCallbackChannel<IChatCallback>();
	        ICommunicationObject communicationObject = (ICommunicationObject) callbackChannel;
            if (m_sessionContext.UserExists(user) || (asMaster && m_sessionContext.MasterUser != null))
            {
                callbackChannel.RegistrationFailed();
                return;
            }

            if (asMaster)
                m_sessionContext.MasterUser = user;

            m_connectionsManager.AddUser(user, callbackChannel);

			communicationObject.Closed += (sender, args) =>
	        {
		        m_connectionsManager.RemoveUser(user);

				if (m_sessionContext.MasterUser != null && m_sessionContext.MasterUser.Equals(user))
			        m_sessionContext.MasterUser = null;
	        };

			communicationObject.Faulted += (sender, args) =>
			{
				m_connectionsManager.RemoveUser(user);

				if (m_sessionContext.MasterUser != null && m_sessionContext.MasterUser.Equals(user))
					m_sessionContext.MasterUser = null;
			};

            callbackChannel.RegistrationCompleted();
        }

        public void UnregisterUser(User user)
        {
            if (!m_sessionContext.UserExists(user))
                return;

            m_connectionsManager.RemoveUser(user);
        }

        public void SendMessage(Guid chatGroupId, User user, string message)
        {
            if (!m_sessionContext.CanUserSendMessageToGroup(user, chatGroupId))
                return;

            ChatGroup chatGroup = m_sessionContext.FindGroupById(chatGroupId);

            if (m_sessionContext.MainChatGroup.Id == chatGroupId)
            {
                foreach (ChatGroup @group in m_sessionContext.Groups)
                {
                    foreach (User existingUser in @group.Users.Values)
                    {
                        m_connectionsManager[existingUser].MessageReceived(user, chatGroupId, message);
                    }
                }
            }
            else
            {
                foreach (User destinationUser in chatGroup.Users.Values)
                    m_connectionsManager[destinationUser].MessageReceived(user, chatGroupId, message);

                if (chatGroupId != m_sessionContext.MainChatGroup.Id &&
                    !chatGroup.Contains(m_sessionContext.MasterUser.Id))
                    m_connectionsManager[m_sessionContext.MasterUser].MessageReceived(user, chatGroupId, message);
            }
        }

        public void SendPrivateMessage(User senderUser, User destinationUser, string message)
        {
            if (!m_sessionContext.UserExists(senderUser) || !m_sessionContext.UserExists(destinationUser))
                return;

            m_connectionsManager[destinationUser].PrivateMessageReceived(senderUser, destinationUser, message);
            m_connectionsManager[senderUser].PrivateMessageReceived(senderUser, destinationUser, message);

            if(!destinationUser.Equals(m_sessionContext.MasterUser) && !senderUser.Equals(m_sessionContext.MasterUser))
                m_connectionsManager[m_sessionContext.MasterUser].PrivateMessageReceived(senderUser, destinationUser, message);
        }

        public void CreateGroup(ChatGroup chatGroup)
        {
            m_sessionContext.AddGroup(chatGroup);
            foreach (ChatGroup @group in m_sessionContext.Groups)
            {
                foreach (User user in @group.Users.Values)
                {
                    m_connectionsManager[user].NewGroupCreated(chatGroup);
                }
            }
        }

        public void MoveUserToGroup(User user, Guid groupId)
        {
            m_sessionContext.RemoveUser(user);
            m_sessionContext.AddUser(user, m_sessionContext.FindGroupById(groupId));

            foreach (ChatGroup @group in m_sessionContext.Groups)
            {
                foreach (User existingUser in @group.Users.Values)
                {
                    m_connectionsManager[existingUser].UserMovedToGroup(user, groupId);
                }
            }
        }

        public void Roll(User user, RollResult rollResult)
        {
            ChatGroup chatGroup = m_sessionContext.FindGroupByUser(user);

            if (chatGroup.Id == m_sessionContext.MainChatGroup.Id)
            {
                foreach (ChatGroup @group in m_sessionContext.Groups)
                {
                    foreach (User existingUser in @group.Users.Values)
                    {
                        m_connectionsManager[existingUser].UserRolled(user, rollResult);
                    }
                }
            }
            else
            {
                foreach (User existingUser in chatGroup.Users.Values)
                {
                    m_connectionsManager[existingUser].UserRolled(user, rollResult);
                }

                if (!chatGroup.Contains(m_sessionContext.MasterUser.Id))
                    m_connectionsManager[m_sessionContext.MasterUser].UserRolled(user, rollResult);
            }
        }

        public void UserInfoChanged(User user)
        {
            ChatGroup chatGroup = m_sessionContext.FindGroupByUser(user);
            if (chatGroup == null)
                return;

            chatGroup.Users[user.Id] = user;

            foreach (ChatGroup @group in m_sessionContext.Groups)
            {
                foreach (User existingUser in @group.Users.Values)
                {
                    m_connectionsManager[existingUser].UserInfoChanged(user);
                }
            }
        }

        public void RequestSessionUpdate()
        {
            IChatCallback callbackChannel = OperationContext.Current.GetCallbackChannel<IChatCallback>();
            callbackChannel.UpdatedSession(m_sessionContext);
        }
    }
}